<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  
  
  <title>vue-nextTick | 漠北烟云</title>
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  <meta name="description" content="弱小和无知从来不是生存的障碍，傲慢才是nextTick 到底是个啥1234567methods: &amp;#123;    update() &amp;#123;        for (let i &#x3D; 0; i &lt; 10; i++) &amp;#123;            this.testNum &#x3D; this.testNum + i;        &amp;#125;    &amp;#125;,&amp;#125;,  Vu">
<meta property="og:type" content="article">
<meta property="og:title" content="vue-nextTick">
<meta property="og:url" content="http://example.com/2023/03/02/vue-nextTick/index.html">
<meta property="og:site_name" content="漠北烟云">
<meta property="og:description" content="弱小和无知从来不是生存的障碍，傲慢才是nextTick 到底是个啥1234567methods: &amp;#123;    update() &amp;#123;        for (let i &#x3D; 0; i &lt; 10; i++) &amp;#123;            this.testNum &#x3D; this.testNum + i;        &amp;#125;    &amp;#125;,&amp;#125;,  Vu">
<meta property="og:locale" content="en_US">
<meta property="article:published_time" content="2023-03-02T14:18:11.000Z">
<meta property="article:modified_time" content="2023-03-29T15:26:43.458Z">
<meta property="article:author" content="mobeiyanyun">
<meta property="article:tag" content="VUE">
<meta name="twitter:card" content="summary">
  
    <link rel="alternate" href="/atom.xml" title="漠北烟云" type="application/atom+xml">
  
  
    <link rel="shortcut icon" href="/favicon.png">
  
  
    
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/typeface-source-code-pro@0.0.71/index.min.css">

  
  
<link rel="stylesheet" href="/css/style.css">

  
    
<link rel="stylesheet" href="/fancybox/jquery.fancybox.min.css">

  
<meta name="generator" content="Hexo 6.3.0"></head>

<body>
  <div id="container">
    <div id="wrap">
      <header id="header">
  <div id="banner"></div>
  <div id="header-outer" class="outer">
    <div id="header-title" class="inner">
      <h1 id="logo-wrap">
        <a href="/" id="logo">漠北烟云</a>
      </h1>
      
    </div>
    <div id="header-inner" class="inner">
      <nav id="main-nav">
        <a id="main-nav-toggle" class="nav-icon"></a>
        
          <a class="main-nav-link" href="/">Home</a>
        
          <a class="main-nav-link" href="/archives">Archives</a>
        
      </nav>
      <nav id="sub-nav">
        
          <a id="nav-rss-link" class="nav-icon" href="/atom.xml" title="RSS Feed"></a>
        
        <a id="nav-search-btn" class="nav-icon" title="Search"></a>
      </nav>
      <div id="search-form-wrap">
        <form action="//google.com/search" method="get" accept-charset="UTF-8" class="search-form"><input type="search" name="q" class="search-form-input" placeholder="Search"><button type="submit" class="search-form-submit">&#xF002;</button><input type="hidden" name="sitesearch" value="http://example.com"></form>
      </div>
    </div>
  </div>
</header>

      <div class="outer">
        <section id="main"><article id="post-vue-nextTick" class="h-entry article article-type-post" itemprop="blogPost" itemscope itemtype="https://schema.org/BlogPosting">
  <div class="article-meta">
    <a href="/2023/03/02/vue-nextTick/" class="article-date">
  <time class="dt-published" datetime="2023-03-02T14:18:11.000Z" itemprop="datePublished">2023-03-02</time>
</a>
    
  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 class="p-name article-title" itemprop="headline name">
      vue-nextTick
    </h1>
  

      </header>
    
    <div class="e-content article-entry" itemprop="articleBody">
      
        <h2 id="弱小和无知从来不是生存的障碍，傲慢才是"><a href="#弱小和无知从来不是生存的障碍，傲慢才是" class="headerlink" title="弱小和无知从来不是生存的障碍，傲慢才是"></a>弱小和无知从来不是生存的障碍，傲慢才是</h2><h5 id="nextTick-到底是个啥"><a href="#nextTick-到底是个啥" class="headerlink" title="nextTick 到底是个啥"></a>nextTick 到底是个啥</h5><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">methods: &#123;</span><br><span class="line">    <span class="function"><span class="title">update</span></span>() &#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="built_in">let</span> i = 0; i &lt; 10; i++) &#123;</span><br><span class="line">            this.testNum = this.testNum + i;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;,</span><br><span class="line">&#125;,</span><br></pre></td></tr></table></figure>

<p>Vue 视图中， testNum 会发生变化。虽然把 firstNum 循环修改了 10 次，但是实际上它只会把最后一次的值更新到视图上——这也是非常合理的。当数据更新发生时，它不会立刻给你执行视图层的更新动作。而是先把这个更新给“存起来”，等到“时机成熟”再执行它；这个“存起来”的地方，叫做异步更新队列；即便一个 watcher 被多次触发，它也只会被推进异步更新队列一次。在同步逻辑执行完之后，watcher 对应的就是其依赖属性的最新的值。最后，Vue 会把异步更新队列的动作集体出队，批量更新。这个实现异步任务派发的接口，就叫做 “nextTick”。</p>
<h5 id="Vue-nextTick-源码"><a href="#Vue-nextTick-源码" class="headerlink" title="Vue-nextTick 源码"></a>Vue-nextTick 源码</h5><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br></pre></td><td class="code"><pre><span class="line">import &#123; noop &#125; from <span class="string">&#x27;shared/util&#x27;</span></span><br><span class="line">import &#123; handleError &#125; from <span class="string">&#x27;./error&#x27;</span></span><br><span class="line">import &#123; isIE, isIOS, isNative &#125; from <span class="string">&#x27;./env&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">export</span> <span class="built_in">let</span> isUsingMicroTask = <span class="literal">false</span></span><br><span class="line"></span><br><span class="line">const callbacks = []</span><br><span class="line"><span class="built_in">let</span> pending = <span class="literal">false</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="function"><span class="title">flushCallbacks</span></span> () &#123;</span><br><span class="line">  pending = <span class="literal">false</span></span><br><span class="line">  const copies = callbacks.slice(0)</span><br><span class="line">  callbacks.length = 0</span><br><span class="line">  <span class="keyword">for</span> (<span class="built_in">let</span> i = 0; i &lt; copies.length; i++) &#123;</span><br><span class="line">    copies[i]()</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">// 用来派发异步任务的函数</span><br><span class="line"><span class="built_in">let</span> timerFunc</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">// 下面这一段逻辑，是根据浏览器的不同，选择不同的 api 来派发异步任务</span><br><span class="line"><span class="keyword">if</span> (typeof Promise !== <span class="string">&#x27;undefined&#x27;</span> &amp;&amp; isNative(Promise)) &#123;</span><br><span class="line">  const p = Promise.resolve()</span><br><span class="line">  timerFunc = () =&gt; &#123;</span><br><span class="line">    p.then(flushCallbacks)</span><br><span class="line">    <span class="keyword">if</span> (isIOS) setTimeout(noop)</span><br><span class="line">  &#125;</span><br><span class="line">  isUsingMicroTask = <span class="literal">true</span></span><br><span class="line">&#125; <span class="keyword">else</span> <span class="keyword">if</span> (!isIE &amp;&amp; typeof MutationObserver !== <span class="string">&#x27;undefined&#x27;</span> &amp;&amp; (</span><br><span class="line">  isNative(MutationObserver) ||</span><br><span class="line">  MutationObserver.toString() === <span class="string">&#x27;[object MutationObserverConstructor]&#x27;</span></span><br><span class="line">)) &#123;</span><br><span class="line">  <span class="built_in">let</span> counter = 1</span><br><span class="line">  const observer = new MutationObserver(flushCallbacks)</span><br><span class="line">  const textNode = document.createTextNode(String(counter))</span><br><span class="line">  observer.observe(textNode, &#123;</span><br><span class="line">    characterData: <span class="literal">true</span></span><br><span class="line">  &#125;)</span><br><span class="line">  timerFunc = () =&gt; &#123;</span><br><span class="line">    counter = (counter + 1) % 2</span><br><span class="line">    textNode.data = String(counter)</span><br><span class="line">  &#125;</span><br><span class="line">  isUsingMicroTask = <span class="literal">true</span></span><br><span class="line">&#125; <span class="keyword">else</span> <span class="keyword">if</span> (typeof setImmediate !== <span class="string">&#x27;undefined&#x27;</span> &amp;&amp; isNative(setImmediate)) &#123;</span><br><span class="line">  timerFunc = () =&gt; &#123;</span><br><span class="line">    setImmediate(flushCallbacks)</span><br><span class="line">  &#125;</span><br><span class="line">&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">  // Fallback to setTimeout.</span><br><span class="line">  timerFunc = () =&gt; &#123;</span><br><span class="line">    setTimeout(flushCallbacks, 0)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">// 暴露 nextTick 方法</span><br><span class="line"><span class="built_in">export</span> <span class="keyword">function</span> nextTick (cb?: Function, ctx?: Object) &#123;</span><br><span class="line">  <span class="built_in">let</span> _resolve</span><br><span class="line">  // 维护一个异步更新队列</span><br><span class="line">  callbacks.push(() =&gt; &#123;</span><br><span class="line">    if (cb) &#123;</span><br><span class="line">      try &#123;</span><br><span class="line">        cb.call(ctx)</span><br><span class="line">      &#125; catch (e) &#123;</span><br><span class="line">        handleError(e, ctx, &#x27;nextTick&#x27;)</span><br><span class="line">      &#125;</span><br><span class="line">    &#125; else if (_resolve) &#123;</span><br><span class="line">      _resolve(ctx)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;)</span><br><span class="line">  // pending 是一个锁，确保任务执行的有序性</span><br><span class="line">  if (!pending) &#123;</span><br><span class="line">    pending = true</span><br><span class="line">    timerFunc()</span><br><span class="line">  &#125;</span><br><span class="line">  // 兜底逻辑，处理入参不是回调的情况</span><br><span class="line">  if (!cb &amp;&amp; typeof Promise !== &#x27;undefined&#x27;) &#123;</span><br><span class="line">    return new Promise(resolve =&gt; &#123;</span><br><span class="line">      _resolve = resolve</span><br><span class="line">    &#125;)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>在上面这段代码里，有三个关键角色：nextTick、timerFunc 和 flushCallbacks，nextTick 是入口函数</p>
<h5 id="逻辑统筹者——nextTick"><a href="#逻辑统筹者——nextTick" class="headerlink" title="逻辑统筹者——nextTick"></a>逻辑统筹者——nextTick</h5><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line">// 暴露 nextTick 方法</span><br><span class="line"><span class="built_in">export</span> <span class="keyword">function</span> nextTick (cb?: Function, ctx?: Object) &#123;</span><br><span class="line">  <span class="built_in">let</span> _resolve</span><br><span class="line">  // 维护一个异步更新队列</span><br><span class="line">  callbacks.push(() =&gt; &#123;</span><br><span class="line">    if (cb) &#123;</span><br><span class="line">      try &#123;</span><br><span class="line">        cb.call(ctx)</span><br><span class="line">      &#125; catch (e) &#123;</span><br><span class="line">        handleError(e, ctx, &#x27;nextTick&#x27;)</span><br><span class="line">      &#125;</span><br><span class="line">    &#125; else if (_resolve) &#123;</span><br><span class="line">      _resolve(ctx)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;)</span><br><span class="line">  // pending 是一个锁，确保任务执行有序、不重复</span><br><span class="line">  if (!pending) &#123;</span><br><span class="line">    pending = true</span><br><span class="line">    timerFunc()</span><br><span class="line">  &#125;</span><br><span class="line">  // 兜底逻辑，处理入参不是回调的情况</span><br><span class="line">  if (!cb &amp;&amp; typeof Promise !== &#x27;undefined&#x27;) &#123;</span><br><span class="line">    return new Promise(resolve =&gt; &#123;</span><br><span class="line">      _resolve = resolve</span><br><span class="line">    &#125;)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>有三个变量需要引起重视</p>
<ul>
<li>callbacks - 异步更新队列</li>
<li>pending - “锁”</li>
<li>timerFunc - 异步任务的派发函数</li>
</ul>
<p>首先，<strong>nextTick 的入参是一个回调函数，这个回调函数就是一个“任务”。</strong>每次 nextTick 接收一个任务，它不会立刻去执行它，而是把它 push 进 callbacks 这个异步更新队列里（也就是存起来）。接着，去检查 pending 的值。这个 pending 有何妙用呢？大家知道，我这个异步更新队列肯定不能一直往里塞东西，我得找个时机把它派发出去对不对？那么如何决定啥时候派发它呢？</p>
<p>如果说这个 pending 为 false，意味着啥？ 意味着“现在还没有一个异步更新队列被派发出去”，那么就调用 timerFunc，把当前维护的这个异步队列给派发出去；那如果 pending 为 true 呢？意味着现在异步更新队列（callbacks）已经被派发出去了，此时 callbacks 已经呆在浏览器的异步任务队列里、确保会被执行了，因此没有必要再执行一遍 timerFunc 去重复派发这个队列，只需要往里面添加任务就可以了。</p>
<h5 id="异步任务派发器——timerFunc"><a href="#异步任务派发器——timerFunc" class="headerlink" title="异步任务派发器——timerFunc"></a>异步任务派发器——timerFunc</h5><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line">// 用来派发异步任务的函数</span><br><span class="line"><span class="built_in">let</span> timerFunc</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">// 下面这一段逻辑，是根据浏览器的不同，选择不同的 api 来派发异步任务</span><br><span class="line"><span class="keyword">if</span> (typeof Promise !== <span class="string">&#x27;undefined&#x27;</span> &amp;&amp; isNative(Promise)) &#123;</span><br><span class="line">  const p = Promise.resolve()</span><br><span class="line">  timerFunc = () =&gt; &#123;</span><br><span class="line">    p.then(flushCallbacks)</span><br><span class="line">    <span class="keyword">if</span> (isIOS) setTimeout(noop)</span><br><span class="line">  &#125;</span><br><span class="line">  isUsingMicroTask = <span class="literal">true</span></span><br><span class="line">&#125; <span class="keyword">else</span> <span class="keyword">if</span> (!isIE &amp;&amp; typeof MutationObserver !== <span class="string">&#x27;undefined&#x27;</span> &amp;&amp; (</span><br><span class="line">  isNative(MutationObserver) ||</span><br><span class="line">  MutationObserver.toString() === <span class="string">&#x27;[object MutationObserverConstructor]&#x27;</span></span><br><span class="line">)) &#123;</span><br><span class="line">  <span class="built_in">let</span> counter = 1</span><br><span class="line">  const observer = new MutationObserver(flushCallbacks)</span><br><span class="line">  const textNode = document.createTextNode(String(counter))</span><br><span class="line">  observer.observe(textNode, &#123;</span><br><span class="line">    characterData: <span class="literal">true</span></span><br><span class="line">  &#125;)</span><br><span class="line">  timerFunc = () =&gt; &#123;</span><br><span class="line">    counter = (counter + 1) % 2</span><br><span class="line">    textNode.data = String(counter)</span><br><span class="line">  &#125;</span><br><span class="line">  isUsingMicroTask = <span class="literal">true</span></span><br><span class="line">&#125; <span class="keyword">else</span> <span class="keyword">if</span> (typeof setImmediate !== <span class="string">&#x27;undefined&#x27;</span> &amp;&amp; isNative(setImmediate)) &#123;</span><br><span class="line">  timerFunc = () =&gt; &#123;</span><br><span class="line">    setImmediate(flushCallbacks)</span><br><span class="line">  &#125;</span><br><span class="line">&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">  // Fallback to setTimeout.</span><br><span class="line">  timerFunc = () =&gt; &#123;</span><br><span class="line">    setTimeout(flushCallbacks, 0)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>关键的点</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">timerFunc = () =&gt; &#123;</span><br><span class="line">  p.then(flushCallbacks)</span><br><span class="line">  <span class="keyword">if</span> (isIOS) setTimeout(noop)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">timerFunc = () =&gt; &#123;</span><br><span class="line">  counter = (counter + 1) % 2</span><br><span class="line">  textNode.data = String(counter)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">timerFunc = () =&gt; &#123;</span><br><span class="line">  setImmediate(flushCallbacks)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">timerFunc = () =&gt; &#123;</span><br><span class="line">  setTimeout(flushCallbacks, 0)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>你会发现，不同的 timerFunc 之间有一个共性——它们都在派发 flushCallbacks 这个函数（这个函数我们下面会讲）。那么不同的 timerFunc 间有啥区别呢？我们看到区别在于派发 flushCallbacks 的方式不同，这里一共有四种派发方式：</p>
<ul>
<li>Promise.then</li>
<li>MutationObserver</li>
<li>setImmediate</li>
<li>setTimeout<br>除去处理浏览器的和一些细节其实事情很简单：<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span>(当前环境支持 Promise)&#123;</span><br><span class="line">    Promise.<span class="keyword">then</span> 派发 timerFunc</span><br><span class="line">&#125; <span class="keyword">else</span> <span class="keyword">if</span> (当前环境支持 MutationObserver) &#123;</span><br><span class="line">        MutationObserver 派发 timerFunc</span><br><span class="line">&#125; <span class="keyword">else</span> <span class="keyword">if</span> (当前环境支持 setImmediate) &#123;</span><br><span class="line">    setImmediate 派发 timerFunc</span><br><span class="line">&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    setTimeout 派发 timer Func</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
我们看到，timerFunc 按照优先级分别可能通过：Promise.then、MutationObserver、setImmediate、setTimeout 四种途径派发。这个优先级比较有看头，大家会发现它是优先派发 micro-task、次选 macro-task。</li>
</ul>
<h5 id="为什么-Vue-优先派发的是-micro-task？"><a href="#为什么-Vue-优先派发的是-micro-task？" class="headerlink" title="为什么 Vue 优先派发的是 micro-task？"></a>为什么 Vue 优先派发的是 micro-task？</h5><p>Event-Loop 的执行流程</p>
<ol>
<li><p>执行并出队一个 macro-task。</p>
</li>
<li><p>全局上下文（script 标签）被推入调用栈，同步代码执行。在执行的过程中，通过对一些接口的调用，可以产生新的 macro-task 与 micro-task，它们会分别被推入各自的任务队列里。这个过程本质上是队列的 macro-task 的执行和出队的过程。</p>
</li>
<li><p>上一步我们出队的是一个 macro-task，这一步我们处理的是 micro-task。但需要注意的是：当 macro-task 出队时，任务是一个一个执行的；而 micro-task 出队时，任务是一队一队执行的。因此，我们处理 micro 队列这一步，会逐个执行队列中的任务并把它出队，直到队列被清空。</p>
</li>
<li><p>执行渲染操作，更新界面；</p>
</li>
<li><p>检查是否存在 Web worker 任务，如果有，则对其进行处理；</p>
</li>
</ol>
<p>现在需要大家特别关注的是 1、2、3、4 之间的关系！大家想，如果我在 2 中派发的是一个 macro-task，那么这个任务会在什么时候被执行？</p>
<p>仔细想想，它是不是会被推移到下一个循环里的 1 中被执行？这就有问题了——如果我的任务是用来更新 UI 界面的，那么它在我当前循环的 4 中并不会被感知；你只能等它在下一个循环中的 1 中生效后，才能在下一个循环的 4 中被感知、进而更新到界面上。</p>
<p>问题出现了：macro-task 形式的派发，会导致我们的界面更新延迟一个事件循环。在当前循环的 4，这个渲染时机一定程度上被“浪费”了，它并不能及时渲染出我们的更新。</p>
<p>此外，micro-task 是一队一队来更新，而 macro-task 是一个一个来更新。从更新效率上来说，micro-task 也会更优秀。</p>
<h5 id="任务执行器——flushCallbacks"><a href="#任务执行器——flushCallbacks" class="headerlink" title="任务执行器——flushCallbacks"></a>任务执行器——flushCallbacks</h5><p>flushCallbacks 的逻辑相对上面简单不少：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="function"><span class="title">flushCallbacks</span></span> () &#123;</span><br><span class="line">  // 把“锁”打开</span><br><span class="line">  pending = <span class="literal">false</span></span><br><span class="line">  // 创造 callbacks 副本，避免副作用</span><br><span class="line">  const copies = callbacks.slice(0)</span><br><span class="line">  // callbacks 队列置空</span><br><span class="line">  callbacks.length = 0</span><br><span class="line">  // 逐个执行异步任务</span><br><span class="line">  <span class="keyword">for</span> (<span class="built_in">let</span> i = 0; i &lt; copies.length; i++) &#123;</span><br><span class="line">    copies[i]()</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>它负责把 callbacks 里的任务逐个取出，依次执行。</p>
<p>注意，进入 flushCallbacks 后，做的第一件事情就是把 pending 置为 false。因为 flushCallbacks 执行完毕后，callbacks 将被清空、浏览器的异步任务队列中也就没有 Vue 的异步任务了。此时必须把 pending 置空，确保下一个 Vue 异步任务队列进来时，可以及时被派发。</p>

      
    </div>
    <footer class="article-footer">
      <a data-url="http://example.com/2023/03/02/vue-nextTick/" data-id="clfziy0om0005p5r412st6fxh" data-title="vue-nextTick" class="article-share-link">Share</a>
      
      
      
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/VUE/" rel="tag">VUE</a></li></ul>

    </footer>
  </div>
  
    
<nav id="article-nav">
  
    <a href="/2023/03/05/JSBridge/" id="article-nav-newer" class="article-nav-link-wrap">
      <strong class="article-nav-caption">Newer</strong>
      <div class="article-nav-title">
        
          JSBridge实现原理
        
      </div>
    </a>
  
  
    <a href="/2023/03/02/%E8%B7%A8%E5%9F%9F%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88/" id="article-nav-older" class="article-nav-link-wrap">
      <strong class="article-nav-caption">Older</strong>
      <div class="article-nav-title">跨域解决方案</div>
    </a>
  
</nav>

  
</article>


</section>
        
          <aside id="sidebar">
  
    

  
    
  <div class="widget-wrap">
    <h3 class="widget-title">Tags</h3>
    <div class="widget">
      <ul class="tag-list" itemprop="keywords"><li class="tag-list-item"><a class="tag-list-link" href="/tags/VUE/" rel="tag">VUE</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/js%E5%9F%BA%E7%A1%80/" rel="tag">js基础</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E5%B0%8F%E7%A8%8B%E5%BA%8F-uniapp/" rel="tag">小程序&#x2F;uniapp</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E6%B7%B7%E5%90%88%E5%BC%80%E5%8F%91/" rel="tag">混合开发</a></li></ul>
    </div>
  </div>


  
    
  <div class="widget-wrap">
    <h3 class="widget-title">Tag Cloud</h3>
    <div class="widget tagcloud">
      <a href="/tags/VUE/" style="font-size: 15px;">VUE</a> <a href="/tags/js%E5%9F%BA%E7%A1%80/" style="font-size: 20px;">js基础</a> <a href="/tags/%E5%B0%8F%E7%A8%8B%E5%BA%8F-uniapp/" style="font-size: 10px;">小程序/uniapp</a> <a href="/tags/%E6%B7%B7%E5%90%88%E5%BC%80%E5%8F%91/" style="font-size: 10px;">混合开发</a>
    </div>
  </div>

  
    
  <div class="widget-wrap">
    <h3 class="widget-title">Archives</h3>
    <div class="widget">
      <ul class="archive-list"><li class="archive-list-item"><a class="archive-list-link" href="/archives/2023/04/">April 2023</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2023/03/">March 2023</a></li></ul>
    </div>
  </div>


  
    
  <div class="widget-wrap">
    <h3 class="widget-title">Recent Posts</h3>
    <div class="widget">
      <ul>
        
          <li>
            <a href="/2023/04/02/%E5%B0%8F%E7%A8%8B%E5%BA%8F%E7%99%BB%E5%BD%95%E8%AE%BE%E8%AE%A1/">小程序登录设计</a>
          </li>
        
          <li>
            <a href="/2023/03/30/%E8%A7%82%E5%AF%9F%E8%80%85%E6%A8%A1%E5%BC%8F/">vue中的观察者模式</a>
          </li>
        
          <li>
            <a href="/2023/03/29/%E6%95%B0%E6%8D%AE%E5%8F%98%E6%9B%B4%E7%9A%84%E7%9B%91%E6%B5%8B/">数据变更的监测</a>
          </li>
        
          <li>
            <a href="/2023/03/26/%E9%97%AD%E5%8C%85/">闭包</a>
          </li>
        
          <li>
            <a href="/2023/03/21/instanceof%E5%8E%9F%E7%90%86/">instanceof原理</a>
          </li>
        
      </ul>
    </div>
  </div>

  
</aside>
        
      </div>
      <footer id="footer">
  
  <div class="outer">
    <div id="footer-info" class="inner">
      
      &copy; 2023 mobeiyanyun<br>
      Powered by <a href="https://hexo.io/" target="_blank">Hexo</a>
    </div>
  </div>
</footer>

    </div>
    <nav id="mobile-nav">
  
    <a href="/" class="mobile-nav-link">Home</a>
  
    <a href="/archives" class="mobile-nav-link">Archives</a>
  
</nav>
    


<script src="/js/jquery-3.4.1.min.js"></script>



  
<script src="/fancybox/jquery.fancybox.min.js"></script>




<script src="/js/script.js"></script>





  </div>
</body>
</html>